package com.raindrop.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
 * BeanAndMapUtil
 * map和实体互转工具类
 *
 * @author liuhang
 * @date 2018/5/16
 */
public class BeanAndMapUtil {
    private static final String CHILDREN_TEXT = "children";

    /**
     * 将列表转换为map树（根据父类key进行递归） 用于权限等列表结构使用
     *
     * @param list
     * @return
     */
    public static List<Map<String, Object>> list2MapTree(List<?> list, String myKey, String key, String root) {
        List<Map<String, Object>> arrays = new ArrayList<>();
        for (Object o : list) {
            Map<String, Object> m = object2Map(o);
            arrays.add(m);
        }
        List<Map<String, Object>> treeList = new ArrayList<>();
        for (Map<String, Object> map : arrays) {
            if (root.equals(map.get(key))) {
                treeList.add(findChildren(map, treeList, myKey, key, root));
            }
        }
        return arrays;
    }

    private static Map<String, Object> findChildren(Map<String, Object> map, List<Map<String, Object>> treeList, String myKey, String key, String root) {
        for (Map<String, Object> m : treeList) {
            if (m.get(myKey).equals(m.get(key))) {
                List<Map<String,Object>> list = null;
                if (!m.containsKey(CHILDREN_TEXT)) {
                    list = new ArrayList<>();
                    map.put(CHILDREN_TEXT,list);
                }
                 list = (List<Map<String, Object>>) map.get(CHILDREN_TEXT);
                list.add(findChildren(m,treeList,myKey,key,root));
                map.put(CHILDREN_TEXT,list);
            }
        }
        return map;
    }

    /**
     * 实体类对象转换成Map
     *
     * @param obj
     * @return
     */
    public static Map<String, Object> object2Map(Object obj) {
        Map<String, Object> reMap = new HashMap<>();
        if (obj == null)
            return null;
        List<Field> list = getAllFieldsList(obj.getClass());
        try {
            for (Field field : list) {
                try {
                    field.setAccessible(true);
                    Object o = field.get(obj);
                    reMap.put(field.getName(), o);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return reMap;
    }

    /**
     * 把Map<String,Object>处理成实体类
     * 根据父类情况  将父类信息一同获取
     *
     * @param clazz 想要的实体类
     * @param map   包含信息的Map对象
     * @return
     */
    public static Object map2Object(Class clazz, Map<String, Object> map) {

        if (null == map) {
            return null;
        }

        List<Field> list = getAllFieldsList(clazz);
        Object o = null;
        try {
            o = clazz.newInstance();
        } catch (InstantiationException e1) {
            e1.printStackTrace();
        } catch (IllegalAccessException e1) {
            e1.printStackTrace();
        }
        for (Field field : list) {
            String fieldName = field.getName();
            //把属性的第一个字母处理成大写
            String stringLetter = fieldName.substring(0, 1).toUpperCase();
            //取得set方法名，比如setBbzt
            String setterName = "set" + stringLetter + fieldName.substring(1);
            //真正取得set方法。
            Method setMethod = null;
            Class fieldClass = field.getType();
            try {
                if (isHaveSuchMethod(clazz, setterName)) {
                    if (fieldClass == String.class) {
                        setMethod = clazz.getMethod(setterName, fieldClass);
                        setMethod.invoke(o, String.valueOf(map.get(fieldName)));//为其赋值
                    } else if (fieldClass == Integer.class || fieldClass == int.class) {
                        setMethod = clazz.getMethod(setterName, fieldClass);
                        setMethod.invoke(o, Integer.parseInt(String.valueOf(map.get(fieldName))));//为其赋值
                    } else if (fieldClass == Boolean.class || fieldClass == boolean.class) {
                        setMethod = clazz.getMethod(setterName, fieldClass);
                        setMethod.invoke(o, Boolean.getBoolean(String.valueOf(map.get(fieldName))));//为其赋值
                    } else if (fieldClass == Short.class || fieldClass == short.class) {
                        setMethod = clazz.getMethod(setterName, fieldClass);
                        setMethod.invoke(o, Short.parseShort(String.valueOf(map.get(fieldName))));//为其赋值
                    } else if (fieldClass == Long.class || fieldClass == long.class) {
                        setMethod = clazz.getMethod(setterName, fieldClass);
                        setMethod.invoke(o, Long.parseLong(String.valueOf(map.get(fieldName))));//为其赋值
                    } else if (fieldClass == Double.class || fieldClass == double.class) {
                        setMethod = clazz.getMethod(setterName, fieldClass);
                        setMethod.invoke(o, Double.parseDouble(String.valueOf(map.get(fieldName))));//为其赋值
                    } else if (fieldClass == Float.class || fieldClass == float.class) {
                        setMethod = clazz.getMethod(setterName, fieldClass);
                        setMethod.invoke(o, Float.parseFloat(String.valueOf(map.get(fieldName))));//为其赋值
                    } else if (fieldClass == BigInteger.class) {
                        setMethod = clazz.getMethod(setterName, fieldClass);
                        setMethod.invoke(o, BigInteger.valueOf(Long.parseLong(String.valueOf(map.get(fieldName)))));//为其赋值
                    } else if (fieldClass == BigDecimal.class) {
                        setMethod = clazz.getMethod(setterName, fieldClass);
                        setMethod.invoke(o, BigDecimal.valueOf(Long.parseLong(String.valueOf(map.get(fieldName)))));//为其赋值
                    } else if (fieldClass == Date.class) {
                        setMethod = clazz.getMethod(setterName, fieldClass);
                        if (map.get(fieldName).getClass() == java.sql.Date.class) {
                            setMethod.invoke(o, new Date(((java.sql.Date) map.get(fieldName)).getTime()));//为其赋值
                        } else if (map.get(fieldName).getClass() == java.sql.Time.class) {
                            setMethod.invoke(o, new Date(((java.sql.Time) map.get(fieldName)).getTime()));//为其赋值
                        } else if (map.get(fieldName).getClass() == java.sql.Timestamp.class) {
                            setMethod.invoke(o, new Date(((java.sql.Timestamp) map.get(fieldName)).getTime()));//为其赋值
                        }
                    }
                }
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

        }
        return o;
    }

    /**
     * 判断某个类里是否有某个方法
     *
     * @param clazz
     * @param methodName
     * @return
     */
    public static boolean isHaveSuchMethod(Class<?> clazz, String methodName) {
        Method[] methodArray = clazz.getMethods();
        boolean result = false;
        if (null != methodArray) {
            for (int i = 0; i < methodArray.length; i++) {
                if (methodArray[i].getName().equals(methodName)) {
                    result = true;
                    break;
                }
            }
        }
        return result;
    }

    public static List<Field> getAllFieldsList(final Class<?> cls) {
        final List<Field> allFields = new ArrayList<>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            final Field[] declaredFields = currentClass.getDeclaredFields();
            for (final Field field : declaredFields) {
                if (!Modifier.isFinal(field.getModifiers())) {
                    allFields.add(field);
                }
            }
            currentClass = currentClass.getSuperclass();
        }
        return allFields;
    }
}
